<?php

/***
 * Class CW_InputClass is a simple object that allows the system to clean all incoming data, making it safe for display or database insertion.
 ***/
class CW_InputClass {

	/***
	 * Multi-dimensional array containing request types and their values.
	 * @access Private
	 * @type Array
	 ***/
	private $input_stack;


   // ! Constructor Method

   /**
	* Instantiates class and defines instance variables.
	*
	* @param None
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Public
	* @return Void
	*/
	public function __construct()
	{
		$this->input_stack = array();
	}


   // ! Accessor Method

   /**
	* Returns a input stack by name.
	*
	* @param $filters Title of stack to return.
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Public
	* @return Array
	*/
	public function getStack($filters)
	{
		if(is_array($filters))
		{
			$input = array();

			foreach($filters as $filter)
			{
				if(isset($this->input_stack[$filter]))
				{
					$input = array_merge($this->input_stack[$filter], $input);
				}
			}

			return $input;
		}

		return isset($this->input_stack[$filters]) ? $this->input_stack[$filters] : array();
	}


   // ! Mutator Method

   /**
	* Creates a new input stack and cleans it's values.
	*
	* @param $stack_title Title of the new input stack.
	* @param $stack_values Array of values to add to the stack.
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Public
	* @return Mixed
	*/
	public function addStack($stack_title, &$stack_values, $clean = true, $xss = false)
	{
		$this->input_stack[$stack_title] = $clean ? self::clean($stack_values, $xss): $stack_values;

		return false;
	}


   // ! Accessor Method

   /**
	* Returns a value of a specified input stack. If
	* the value can not be found, a default value may be
	* passed and returned instead.
	*
	* @param $name Name of the value to return.
	* @param $default The default value to return if no value is found.
	* @param $input_type The input stack that contains the specified value.
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Public
	* @return Mixed
	*/
	public function get($name, $default = false, $input_type = 'get')
	{
		if(isset($this->input_stack[$input_type][$name]))
		{
			return $this->input_stack[$input_type][$name] ? $this->input_stack[$input_type][$name] : $default;
		}

		return $default;
	}


   // ! Accessor Method

   /**
	* Checks if a key exists within the specified input stack.
	*
	* @param $name Name of the key to check
	* @param $input_type The input stack that contains the specified key.
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Public
	* @return Mixed
	*/
	public function exists($name, $input_type = 'get')
	{
		return isset($this->input_stack[$input_type][$name])? true : false;
	}



   // ! Mutator Method

   /**
	* Assigns a value to an existing input.
	*
	* @param $name Name of the value to return.
	* @param $value Value of new input
	* @param $input_type The input stack that contains the specified value.
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Public
	* @return Bool
	*/
	public function set($name, $value = '', $input_type = 'get')
	{
		$this->input_stack[$input_type][$name] = $value;

		return true;
	}


   // ! Mutator Method

   /**
	* Assigns a value to an existing input.
	*
	* @param $name Name of the value to return.
	* @param $value Value of new input
	* @param $input_type The input stack that contains the specified value.
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Public
	* @return Bool
	*/
	public function remove($name, $input_type = 'get')
	{
		unset($this->input_stack[$input_type][$name]);

		return true;
	}


   // ! Executor Method

   /**
	* Ensures that the given string is XSS-Safe
	*
	* @param $string The string to clean.
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Public
	* @return String
	*/
	static public function cleanXss($string)
	{
		if(is_array($string))
		{
			while(list($key) = each($string))
			{
				$string[$key] = $this->xss_clean($string[$key]);
			}

			return $string;
		}

		$string = html_entity_decode($string, ENT_COMPAT, 'utf-8');
		$string = preg_replace('#(&\#*\w+)[\x00-\x20]\+;#U',"$1;", $string);
		$string = preg_replace('#(&\#x*)([0-9A-F]\+);*#iu',"$1$2;", $string);
		$string = preg_replace('#(<[^>]+[\s\r\n\"\'])(on|xmlns)[^>]*>#iU',"$1>", $string);
		$string = preg_replace('#([a-z]\*)[\x00-\x20]\*=[\x00-\x20]\*([\`\'\"]\*)[\\x00-\x20]\*j[\x00-\x20]\*a[\x00-\x20]\*v[\x00-\x20]\*a[\x00-\x20]\*s[\x00-\x20]\*c[\x00-\x20]\*r[\x00-\x20]\*i[\x00-\x20]\*p[\x00-\x20]\*t[\x00-\x20]\*:#iU', '$1=$2nojavascript...', $string);
		$string = preg_replace('#([a-z]\*)[\x00-\x20]\*=([\'\"]\*)[\x00-\x20]\*v[\x00-\x20]\*b[\x00-\x20]\*s[\x00-\x20]\*c[\x00-\x20]\*r[\x00-\x20]\*i[\x00-\x20]\*p[\x00-\x20]\*t[\x00-\x20]\*:#iU', '$1=$2novbscript...', $string);
		$string = preg_replace('#</*\w+:\w[^>]*>#i', '', $string);

		do
		{
			$clean = $string;

			$string = preg_replace('#</*(applet|meta|xml|blink|link|style|script|embed|object|iframe|frame|frameset|ilayer|layer|bgsound|title|base)[^>]*>#i','',$string);
		}
		while($clean != $string);

		do
		{
			$clean = $string;

			$string = preg_replace(array('/%0[0-8]/', '/[\x00-\x08]/','/%11/', '/\x0b/', '/%12/', '/\x0c/', '/%1[4-9]/', '/%2[0-9]/', '/%3[0-1]/', '/[\x0e-\x1f]/'), '', $string);
		}
		while ($clean != $string);

		return $string;
	}


   // ! Executor Method

   /**
	* Iterates a provided array and cleans
	* both keys and corresponding values.
	*
	* @param $array Array to iterate.
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Public
	* @return Array
	*/
	static public function clean($stack, $xss = true)
	{
		foreach($stack as $key => $value)
		{
			if($xss)
			{
				$value = self::cleanXss($value);
			}

			if(false == is_array($value))
			{
				$val = self::cleanValues($value);

				$stack[$key] = $val;

				if(false == self::cleanKeys($key))
				{
					unset($stack[$key]);
				}
			}
			else
			{
				$stack[$key] = self::clean($value, $xss);
			}
		}

		return $stack;
	}


   // ! Executor Method

   /**
	* Cleans an array item's key.
	*
	* @param $key String to clean.
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Private
	* @return Bool
	*/
	static private function cleanKeys($key)
	{
		return preg_match('#^[a-z0-9:_\/-]+$#i', $key);
	}


   // ! Executor Method

   /**
	* Cleans an array item's value.
	*
	* @param $value String to clean.
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Private
	* @return String
	*/
	static private function cleanValues($value)
	{
		if(false == $value)
		{
			return '';
		}

		$value = str_replace(array('&', '>', '<', '"', '\''), array('&amp;', '&gt;', '&lt;', '&quot;', '&#39;'), $value);

		$value = preg_replace("/&amp;#([0-9]+);/si",  "&#\\1;", $value);
		$value = preg_replace("/\\\(?!&amp;#|\?#)/", "&#092;", $value);

		return $value;
	}
}

?>